<script lang="ts" setup>
import lodash from 'lodash'
import moment from 'moment'
import html2canvas from 'html2canvas'
import { Button, DropdownItem, DropdownMenu, Field, Tag, showConfirmDialog, showDialog, showSuccessToast } from 'vant'
import { FontAwesomeIcon } from '@fortawesome/vue-fontawesome'
import { OrderApi, OrderFilterBatch } from '~/api/OrderApi'
import LxPop from '~/components/common/LxPop.vue'
import OrderBriefTable from '~/components/order/OrderBriefTable.vue'
import ContactPick from '~/components/order/edit/ContactPick.vue'
import MaterialPick from '~/components/order/edit/MaterialPick.vue'
import DateRangePick from '~/components/order/edit/DateRangePick.vue'
import { ContactModel, MaterialModel, OrderItemModel, OrderModel } from '~/model'
import LxEntry from '~/components/common/LxEntry.vue'
import { OrderSummary } from '~/common'
import { pickDate, protectedAction } from '~/utils'
import { ContactApi } from '~/api/ContactApi'
import { baseUrl } from '~/api'
import { Paged } from '~/api/Paged'

/**
 * 组件名称（KeepAlive用
 */

defineOptions({
  name: 'OrdersIndex',
})

/**
 * Router
 */

const route = useRoute()
const router = useRouter()

/**
 * 判断是否是移动端
 */
const isMobile = ref(false)
onMounted(() => {
  const userAgentInfo = navigator.userAgent
  const mobileAgents = ['Android', 'iPhone', 'SymbianOS', 'Windows Phone', 'iPad', 'iPod']
  const mobileFlag = mobileAgents.some((mobileAgent) => {
    return userAgentInfo.indexOf(mobileAgent) > 0
  })
  isMobile.value = mobileFlag
})

const orders = ref([] as OrderModel[])
const totalNumber = ref(0)
const page = ref(1)

// 根据era/invalid等外部条件对加载进行注入
function loadData(page_num?: number, per_page?: number): Promise<Paged<OrderModel>> {
  // 复制原来的filter
  const filters = lodash.cloneDeep(filterBatch.value)

  // 注入era
  if (era.value !== 0) {
    filters.era = era.value.toString()
  }

  // 注入invalid
  if (invalid.value) {
    filters.invalid = '1'
  }

  return OrderApi.getPaged(page_num, per_page, filters)
}

// 预设的页面大小
const PAGE_SIZE = 20

// 根据预设的页面大小加载数据
async function loadPage(page_num: number): Promise<Paged<OrderModel>> {
  return loadData(page_num, PAGE_SIZE)
}

async function loadPage1() {
  page.value = 1
  const paged = await loadPage(page.value)
  orders.value = paged.objects
  totalNumber.value = paged.total_number
}

async function loadNextPage() {
  // 检查：加载完毕
  if (orders.value.length === totalNumber.value) {
    return
  }
  page.value += 1
  const paged = await loadPage(page.value)
  orders.value.push(...paged.objects)
}

async function loadAll() {
  page.value = 1
  const paged = await loadData(1, 10000000000)
  orders.value = paged.objects
  totalNumber.value = paged.total_number
}

// type OrderModelFilter = (item: OrderModel) => boolean
type OrderModelFilterNamespace = (
  ''
  | 'material'
  | 'date'
  | 'consumer'
  | 'settled'
)
/**
 * 具名过滤器
 * 1. 具有互斥功能
 * 2. 带有显示名
 */
class NamedFilter {
  /** 显示的名称 */
  title: string

  /** 相同命名空间的过滤器互斥 */
  namespace: OrderModelFilterNamespace

  /** 参数 */
  param: string

  constructor() {
    this.param = ''
    this.namespace = ''
    this.title = ''
  }
}
const searchInput = useSessionStorage('order-search', '')
const searchFilter = ref(searchInput.value)

const filterArr = ref([] as NamedFilter[])
const filterBackup = useSessionStorage('order-filterBackup', '[]')
watch(filterArr, () => {
  // if (filterArr.value.length) {
  //   filterBackup.value = JSON.stringify(filterArr.value)
  // }
  filterBackup.value = JSON.stringify(filterArr.value)
}, {
  deep: true,
})

const filterBatch = computed(() => {
  const batch = {} as OrderFilterBatch
  for (const filter of filterArr.value) {
    if (filter.namespace === 'date') {
      const dataArr = filter.param.split(',')
      batch.date_start = dataArr[0]
      batch.date_end = dataArr[1]
    }
    else {
      (batch as any)[filter.namespace] = filter.param
    }
  }
  batch.search = searchFilter.value
  // console.log(batch);

  return batch
})

watch(filterBatch, () => {
  console.log('重新加载')
  loadPage1()
})

/**
 * 过滤选项：用来创建过滤器
 */
interface FilterOption {
  /** 相同命名空间的过滤器互斥 */
  namespace: OrderModelFilterNamespace

  /** 显示的名称 */
  title: string

  /** 用于创建过滤器的行为 */
  action: () => void
}
const filterOptions = ref([
  {
    namespace: 'consumer',
    title: '用户',
    action: () => showConsumerFilter(),
  },
  {
    namespace: 'date',
    title: '今天',
    action: () => filterAdd(
      'date',
      '今天',
      (function () {
        const today = moment().format('YYYY-MM-DD')
        const nextDay = moment((new Date(today)).valueOf() + 86400 * 1000).format('YYYY-MM-DD')
        // console.log(`${today},${nextDay}`);
        return `${today},${nextDay}`
      })(),
    ),
  },
  {
    namespace: 'date',
    title: '某天',
    action: () => showDayFilter(),
  },
  {
    namespace: 'date',
    title: '时间段',
    action: () => showDateFilter(),
  },
  {
    namespace: 'item',
    title: '商品',
    action: () => showMaterialFilter(),
  },
  {
    namespace: 'settled',
    title: '未结账',
    action: () => filterAdd('settled', '未结账', '0'),
  },
  {
    namespace: 'settled',
    title: '已结账',
    action: () => filterAdd('settled', '已结账', '1'),
  },
] as FilterOption[])

interface SorterOption {
  text: string
  value: number
  sorter: (a: OrderModel, b: OrderModel) => number
}

const sorterOption: Ref<SorterOption[]> = ref([
  {
    text: '日期顺序',
    value: 0,
    sorter: (a: OrderModel, b: OrderModel) => b.date.valueOf() - a.date.valueOf(),
  },
  {
    text: '创建顺序',
    value: 1,
    sorter: (a: OrderModel, b: OrderModel) => b.id - a.id,
  },
] as SorterOption[])
const activeSorterIndex = useSessionStorage('orders-activeSorterIndex', 0)
const ordersSorted = computed(() => {
  const _orders = lodash.cloneDeep(orders.value)
  _orders.sort(sorterOption.value[activeSorterIndex.value].sorter)
  return _orders
})

function filterAdd(namespace: OrderModelFilterNamespace, title: string, param: string) {
  const named = new NamedFilter()
  named.namespace = namespace
  named.title = title
  named.param = param
  filterArr.value.push(named)
}

const invalid = ref(false)
const era = ref(0)

onMounted(async () => {
  // await loadPage1()
  // orders.value.sort((a, b) => b.id - a.id)

  // testcase below
  // filterAdd('consumer', '用户:谷亚奇', x => x.consumer?.name === '谷亚奇')

  // 筛选：用户
  const s_userId = route.query.user as string | undefined
  if (s_userId) {
    const userid = Number.parseInt(s_userId!)
    const allConsumers = await ContactApi.getAll()
    const user = allConsumers.find(x => x.id === userid)
    if (user) {
      filterBackup.value = ''
      setTimeout(() => {
        filterAdd('consumer', `${user.name}`, user.id.toString())
        searchInput.value = ''
      })
    }
  }

  // 筛选：年份
  const s_era = route.query.era as string | undefined
  if (s_era) {
    era.value = Number.parseInt(s_era)
  }

  // 筛选：作废
  const s_invalid = route.query.invalid as string | undefined
  if (s_invalid) {
    console.log(`s_invalid = true <${s_invalid}>`);
    
    invalid.value = true
  }

  // 反序列化保存的过滤器
  try {
    const arr = JSON.parse(filterBackup.value) as NamedFilter[]
    arr.forEach((named) => {
      const _named = new NamedFilter()
      _named.namespace = named.namespace
      _named.title = named.title
      _named.param = named.param

      if (!filterArr.value.map(x => x.namespace).includes(_named.namespace)) {
        filterArr.value.push(_named)
      }
    })

    // FEATURE: 由于默认是没有筛选器的，而自动获取数据依赖于筛选器变化或者翻页
    // 所以如果保存的筛选器为空需要手动加载一下
    if (filterArr.value.length === 0) {
      loadPage1()
    }
  }
  catch (err) {
    console.error(err)
    console.error('反序列化筛选器时出错')
  }

  // console.log('所有订单')
  // console.log(orders.value)
  const warningOrder = orders.value.filter((order) => {
    const itemsId = order.items.map(x => x.material?.id)
    if (itemsId.length !== Array.from(new Set(itemsId)).length) {
      return true
    }
    return false
  })
  if (warningOrder.length) {
    console.warn('警告：订单物品重复')
    console.warn(warningOrder)
  }
})

function onRemoveFilter(filter: NamedFilter) {
  const index = filterArr.value.indexOf(filter)
  filterArr.value.splice(index, 1)
}

const popFilterShown = ref(false)

const unusedFilterOptionsGrouped = computed(() => {
  const usedNames = filterArr.value.map(x => x.namespace)
  const unusedFilter = filterOptions.value.filter(x => !usedNames.includes(x.namespace))
  const grouped = lodash.groupBy(unusedFilter, x => x.namespace)
  return grouped
})

function useOption(option: FilterOption) {
  popFilterShown.value = false
  option.action()
}

/**
 *
 * 筛选用户
 *
 */
const pickingConsumer = ref(false)
function pickConsumer(val: ContactModel) {
  filterAdd('consumer', `${val.name}`, val.id.toString())
}
function showConsumerFilter() {
  pickingConsumer.value = true
}

/**
 *
 * 筛选日期
 *
 */
const pickingDate = ref(false)
function pickDateRange(d1: number, d2: number) {
  const timeDisplay = (ts: number) => moment(ts).format('MM/DD')
  const timeParam = (ts: number) => moment(ts).format('YYYY-MM-DD')
  // filterAdd('date', `日期:${timeDisplay(d1)}-${timeDisplay(d2)}`, x => x.date.valueOf() >= d1 && x.date.valueOf() <= d2 + 86400 * 1000)
  filterAdd('date', `${timeDisplay(d1)}-${timeDisplay(d2)}`, `${timeParam(d1)},${timeParam(d2 + 86400 * 1000)}`)
}
function showDateFilter() {
  pickingDate.value = true
}

/**
 *
 * 筛选日期(一天)
 *
 */
function pickDay(d1: number) {
  const today = moment(d1).format('YYYY-MM-DD')
  const nextDay = moment((new Date(today)).valueOf() + 86400 * 1000).format('YYYY-MM-DD')
  filterAdd('date', `${today}`, `${today},${nextDay}`)
}
async function showDayFilter() {
  const d = await pickDate()
  if (d) {
    pickDay(d.valueOf())
  }
}

/**
 *
 * 筛选商品
 *
 */
const pickingMaterial = ref(false)
function pickMaterial(val: MaterialModel) {
  filterAdd('material', `${val.fullName}`, val.id.toString())
  console.log(filterArr.value)
}
function showMaterialFilter() {
  pickingMaterial.value = true
}

/**
 *
 * 菜单
 *
 */
async function checkConsumer(): Promise<boolean> {
  const consumers = new Set(
    orders.value.map(x => x.consumer ? x.consumer.id : null),
  )
  if (consumers.size !== 1) {
    try {
      await showConfirmDialog({
        message: '导出订单涉及多用户，请确认!',
      })
      return true
    }
    catch {
      return false
    }
  }
  return true
}
const menuShown = ref(false)
const isDetail = useSessionStorage('orders-isdetail', false)
function toggleView() {
  isDetail.value = !isDetail.value
  menuShown.value = false
}
async function exportExcel() {
  await loadAll()
  const ok = await checkConsumer()
  if (!ok) {
    return
  }
  const summary = new OrderSummary(orders.value)
  summary.exportExcelDetail()
  menuShown.value = false
}

/**
 *
 * 汇总
 *
 */
const summary = ref(null as null | OrderSummary)
const summarySource = ref(null as any)
const summaryShowing = ref(false)
const summarySrc = ref('')
async function showSummary(asImage = true) {
  summarySrc.value = ''
  const ok = await checkConsumer()
  if (!ok) {
    return
  }
  await loadAll()
  menuShown.value = false
  summary.value = new OrderSummary(orders.value)
  summaryShowing.value = true

  if (asImage) {
    setTimeout(() => {
      html2canvas(summarySource.value)
        .then((canvas) => {
          summarySrc.value = canvas.toDataURL()
        })
    }, 200)
  }
}

/**
 *
 * 存根
 *
 */

async function exportStub() {
  await loadAll()
  const _orders = lodash.cloneDeep(orders.value)
  _orders.sort((a, b) => a.id - b.id)
  const commands = _orders.map((x) => {
    if (!x.stub) {
      return `# 批次${x.batch} 没有存根`
    }
    else {
      const postfix = x.stub.split('.').reverse()[0]
      return `curl -o 批次${x.batch}.${postfix} ${baseUrl}${x.stub}`
    }
  })
  console.log(commands.join('\n'))
  showSuccessToast('请在电脑上查看控制台')
  menuShown.value = false
}

/**
 *
 * 批量清算
 *
 */
function onSettleBatch() {
  const consumerAffected = Array.from(new Set(orders.value.map(x => x.consumer?.id))).length
  if (consumerAffected === 0) {
    showDialog({ message: '没有要执行操作的订单' })
    menuShown.value = false
    return
  }
  if (consumerAffected > 1) {
    showDialog({ message: '同时只能对单个用户批量操作' })
    menuShown.value = false
    return
  }
  const totalBatch = orders.value.length
  const unSettled = orders.value.filter(x => !x.settled)
  const unSettledBatch = unSettled.length
  summary.value = new OrderSummary(unSettled)
  menuShown.value = false
  protectedAction(settleBatch, `订单 ${totalBatch} 笔，其中 ${unSettledBatch} 笔未付款，总计 ¥${summary.value?.totalAmount} ，确认货款收到，并标记为结清吗？`, 1)
  // dialogSettle.value.show()
}
async function settleBatch() {
  if (!summary.value) {
    return
  }
  const ok = await OrderApi.settleBatch(summary.value.orders.map(x => x.id))
  if (ok) {
    showSuccessToast('设置成功')
    window.setTimeout(() => {
      router.go(0)
    }, 500)
  }
  else {
    showSuccessToast('设置失败，请联系管理员')
  }
}

function goCreateOrder() {
  router.push('/orders/choose_template')
}

const stubMaterial = new MaterialModel()
const stubOim = new OrderItemModel()
stubOim.material = stubMaterial

function searchConfirm() {
  searchFilter.value = searchInput.value
}

function conditionReset() {
  searchInput.value = ''
  searchConfirm()

  filterArr.value = []
}

// 记录切换页面时，列表的位置
const scrollContainer = ref(null as (HTMLElement | null))
const scrollContainerScrolled = ref(0)
function scrollContainerHandler() {
  const val = scrollContainer.value?.scrollTop
  if (val) {
    scrollContainerScrolled.value = val
  }
}

// 检验query是否变化，避免页面不能正确的重新加载
const savedQuery = ref(0 as any)

onActivated(() => {
  console.log('######### onActivated');
  console.log();

  // 刷新进入页面时不检验query是否变化
  if (savedQuery.value === 0) {
    savedQuery.value = JSON.stringify(route.query)
  }
  // 复用页面时进行检测，如果路由变化，代表外部筛选条件进行了变化
  // 不光页面要刷新，查找条件也要清空
  else {
    if (savedQuery.value !== JSON.stringify(route.query)) {
      conditionReset()
      history.go(0)
    }
  }
  
  
  if (scrollContainerScrolled.value && scrollContainer.value) {
    scrollContainer.value.scrollTop = scrollContainerScrolled.value
  }
})

const isCalculateShown = ref(false)
async function showCalculate() {
  summarySrc.value = ''

  await loadAll()
  menuShown.value = false
  const _orders = lodash.cloneDeep(orders.value)
  _orders.forEach(order => {
    order.items = order.items.filter(item => item.material!.name.includes(searchInput.value))
  })
  summary.value = new OrderSummary(_orders, false)
  summaryShowing.value = true
  isCalculateShown.value = true

  setTimeout(() => {
    html2canvas(summarySource.value)
      .then((canvas) => {
        summarySrc.value = canvas.toDataURL()
      })
  }, 200)
}
watch(summaryShowing, val => {
  if(!val) {
    isCalculateShown.value = false
  }
})
</script>

<template>
  <div h-screen flex flex-col>
    <MaterialPick
      v-model:show="pickingMaterial"
      :row="stubOim"
      :show-delete="false"
      :show-current="false"
      :show-taken-off="true"
      @update:material="pickMaterial"
    />
    <ContactPick
      v-model="pickingConsumer"
      @update:consumer="pickConsumer"
    />
    <DateRangePick
      v-model="pickingDate"
      @update:date-range="pickDateRange"
    />

    <div v-if="invalid" bg-orange text-center @click="loadAll">
      回收站
    </div>

    <div v-if="era" bg-orange text-center @click="loadAll">
      {{ era }} 年订单
    </div>

    <div
      v-if="orders.length && orders.length === totalNumber"
      bg-green text-center text-white
    >
      全部加载完成: {{ orders.length }} / {{ totalNumber }}
    </div>
    <div v-else bg-yellow text-center @click="loadAll">
      加载: {{ orders.length }} / {{ totalNumber }}
    </div>

    <div w-full flex items-center py2>
      <div b="1px solid blue" mx-2 flex flex-1 of-hidden b-rd-4>
        <Field
          v-model="searchInput"
          placeholder="搜索订单(商品/客户)" b-rd-lb-4 b-rd-lt-4
        />
        <Button type="primary" w-20 b-rd-0 @click="searchConfirm">
          搜索
        </Button>
      </div>

      <img
        src="/icons/menu.png"
        mx2 ml-auto block h6 w6
        @click="() => menuShown = true"
      >
    </div>
    <div mb2 flex items-stretch of-hidden bg-white shadow-2xl>
      <DropdownMenu w="25%">
        <DropdownItem v-model="activeSorterIndex" :options="sorterOption" />
      </DropdownMenu>
      <div flex flex-1 items-center>
        <div text-size-3.5 c-blue @click="popFilterShown = true">
          <span b-r="1px solid reyalblue" mx2 pr2>筛选</span>
          <span>{{ filterArr.map(x => x.title).join(' ') }}</span>
        </div>
      </div>

      <Button mb0 h="100%" b-rd-0 @click="conditionReset">
        重置
      </Button>
    </div>

    <LxPop v-model="popFilterShown" container-color="#fff">
      <div mb>
        当前条件
      </div>
      <div v-if="!filterArr.length" mb text-size-4 text-gray-3>
        无筛选条件
      </div>
      <div v-else flex flex-wrap>
        <Tag
          v-for="(item, index) in filterArr"
          :key="index"
          closable mb2 mr2
          size="large"
          type="primary"
          @click="onRemoveFilter(item)"
        >
          <span mr2>{{ item.title }}</span>
          <FontAwesomeIcon icon="xmark" />
        </Tag>
      </div>

      <div mb>
        筛选
      </div>

      <div
        v-for="(group, gi) in Object.values(unusedFilterOptionsGrouped)"
        :key="gi"
        b-b="1 gray-1 solid" mb-2 flex flex-wrap pb2
      >
        <Tag
          v-for="(option, oi) in group" :key="oi"
          size="large" mr2
          type="primary"
          @click="useOption(option as FilterOption)"
        >
          {{ (option as FilterOption).title }}
        </Tag>
      </div>

      <div
        v-if="!Object.keys(unusedFilterOptionsGrouped).length"
        text-gray-5
      >
        没有可用的筛选
      </div>
    </LxPop>
    <LxPop v-model="summaryShowing" :default-container="false">
      <div flex-cc flex-col>
        <table v-if="summary && !summarySrc" ref="summarySource"
          flex-shrink-0 class="table-summary" :class="{'table-calc': isCalculateShown}" @click.stop>
          <tr>
            <td>商品</td>
            <td text-right>
              单价
            </td>
            <td text-right>
              数量
            </td>
            <td text-right>
              金额
            </td>
          </tr>
          <tr
            v-for="(_, i) in summary.items"
            :key="i"
          >
            <td>{{ summary.items[i] }}</td>
            <td text-right>
              {{ summary.unitPrices[i] }}
            </td>
            <td text-right>
              {{ summary.quantityOfItems[i] }}
            </td>
            <td text-right>
              {{ summary.amountsOfItem[i] }}
            </td>
          </tr>
          
          <tr v-if="isCalculateShown">
            <td colspan="2">
              总计
            </td>
            <td text-right>
              {{ summary.totalQuantity }}
            </td>
            <td text-right>
              {{ summary.totalAmount }}
            </td>
          </tr>
          <tr v-else>
            <td colspan="3">
              总计
            </td>
            <td text-right>
              {{ summary.totalAmount }}
            </td>
          </tr>
        </table>
        <img v-if="summarySrc" mb :src="summarySrc" alt="" class="summary-image">
        <div v-if="summarySrc">
          长按图片进行分享
        </div>
      </div>
    </LxPop>
    <LxPop v-model="menuShown">
      <div flex flex-wrap>
        <!-- <LxEntry title="创建订单" icon="create-order" :action="goCreateOrder" :grid="3" /> -->
        <LxEntry icon="show-property" title="切换视图" :action="toggleView" :grid="3" />
        <LxEntry icon="export-excel" title="导出到Excel" :action="exportExcel" :grid="3" />

        <LxEntry icon="overview" title="消费汇总" :action="() => showSummary(true)" :grid="3" />

        <LxEntry icon="table" title="销售统计" :action="showCalculate" :grid="3" />
      </div>
      <div v-if="!isMobile" flex flex-wrap>
        <LxEntry icon="overview" title="消费汇总(表格)" :action="() => showSummary(false)" :grid="3" />
        <LxEntry icon="insert-raster-image" title="导出存根" :action="exportStub" :grid="3" />
      </div>
      <div v-else text-center text-size-sm text-gray-5>
        桌面端功能已隐藏
      </div>
    </LxPop>
    <div v-if="!orders.length" text-center text-gray-5>
      订单筛选结果为空
    </div>
    <div v-else-if="isDetail">
      <ItemsView
        v-for="(order, index) in ordersSorted"
        :key="index"
        :order="order"
        :header="true"
      />
    </div>
    <div v-else ref="scrollContainer" v-infinite-scroll="loadNextPage" :infinite-scroll-distance="120" flex-1 overflow-auto @scroll="scrollContainerHandler">
      <OrderBriefTable :orders="ordersSorted" />
    </div>
  </div>
</template>

<style scoped lang="scss">
.table-summary {
  background-color: lightskyblue;
  border-collapse: collapse;

  tr:nth-child(1) {
    background-color: dodgerblue;
  }

  td {
    border: 1px solid #000;
    padding: 0.25rem 0.5rem
  }
}

.table-calc {
  background-color: mediumseagreen;
  border-collapse: collapse;

  tr:nth-child(1) {
    background-color: seagreen;
  }

  td {
    border: 1px solid #000;
    padding: 0.25rem 0.5rem
  }
}

.summary-image {
  width: 80%;
  // position: fixed;
  // left: 50%;
  // top: 50%;
  // transform: translate(-50%, -50%);
}
</style>

<route lang="yaml">
meta:
  title: 订单
  keepAlive: true
</route>
